/* Copyright 2013 ESRI
* 
* All rights reserved under the copyright laws of the United States
* and applicable international laws, treaties, and conventions.
* 
* You may freely redistribute and use this sample code, with or
* without modification, provided you include the original copyright
* notice and use restrictions.
* 
* See the use restrictions at <your ArcGIS install location>/DeveloperKit10.2/userestrictions.txt.
* 
*/
package arcgissamples.geometry;

import java.io.File;
import java.io.IOException;

import com.esri.arcgis.datasourcesfile.ShapefileWorkspaceFactory;
import com.esri.arcgis.geodatabase.IFeature;
import com.esri.arcgis.geodatabase.IFeatureBuffer;
import com.esri.arcgis.geodatabase.IFeatureClass;
import com.esri.arcgis.geodatabase.IFeatureCursor;
import com.esri.arcgis.geodatabase.IField;
import com.esri.arcgis.geodatabase.IFields;
import com.esri.arcgis.geodatabase.SpatialFilter;
import com.esri.arcgis.geodatabase.Workspace;
import com.esri.arcgis.geodatabase.esriFieldType;
import com.esri.arcgis.geodatabase.esriSpatialRelEnum;
import com.esri.arcgis.geometry.Envelope;
import com.esri.arcgis.geometry.IGeometry;
import com.esri.arcgis.geometry.ITopologicalOperator;
import com.esri.arcgis.system.AoInitialize;
import com.esri.arcgis.system.EngineInitializer;
import com.esri.arcgis.system.esriLicenseProductCode;
import com.esri.arcgis.system.esriLicenseStatus;

/**
 * Description: This sample takes a source shapefile and the name of a target shapefile, along with the extents of a
 * clipping envelope. These are used to export the features inside the clipping envelope to the newly created target
 * shapefile. Any feature crossing the clipping envelope is clipped. Inputs: Shapefile - input shapefile dataset.
 * Outputs: Shapefile - a new shapefile representing the new clip extent.
 */
public class ClipShapefileByEnvelope
{
	public ClipShapefileByEnvelope()
	{
		
	}

	public static void main(String[] args)
	{
		System.out.println("Starting ClipShapefileByEnvelope - An ArcObjects Java SDK Developer Sample");
		try
		{
			// Initialize the engine and licenses.
			EngineInitializer.initializeEngine();

			AoInitialize aoInit = new AoInitialize();
			initializeArcGISLicenses(aoInit);

			//Get DEVKITHOME Home
			String devKitHome = System.getenv("AGSDEVKITJAVA");

			if (!(new File(devKitHome).exists()))
			{
				System.out.println(devKitHome + " does not exist.\nExiting...");
				System.exit(-1);
			}
			final String srcShapefilePath = devKitHome + "java" + File.separator + 
			"samples" + File.separator + 
			"data" + File.separator + 
			"usa";
			
			final String srcShapefileName = "states.shp";
			final String clippedShapefilePath = getOutputDir() + File.separator + "clipbyenvelope";
			final String clippedShapefileName = "clipped.shp";
			final double llLng = -115.216;
			final double llLat = 44.3;
			final double urLng = -92.45;
			final double urLat = 35.67;
	
			File clippedShapefileFile = new File(clippedShapefilePath, clippedShapefileName);
			if (clippedShapefileFile.exists())
			{
				System.out.println("Output shapefile already exists: " + clippedShapefileFile.getAbsolutePath());
				System.out.println("Delete it (plus related files) and rerun");
				System.exit(0);
			}
			File clippedDir = new File(clippedShapefilePath);
			clippedDir.mkdir();			

			IFeatureClass clippedFeatureClass = clip(srcShapefilePath, srcShapefileName, clippedShapefilePath,
					clippedShapefileName, llLng, llLat, urLng, urLat);
			System.out.println("Created " + clippedShapefileFile.getAbsolutePath());
			System.out.println("It contains " + clippedFeatureClass.featureCount(null) + " feature(s).");
			aoInit.shutdown();
		}
		catch (IOException e)
		{
			e.printStackTrace();
			System.out.println(e.getMessage());
			System.out.println("Sample failed.  Exiting...");
			System.exit(-1);
		}
	}
	
	/**
	 * Clips shapefile
	 * @param srcShapefilePath
	 * @param srcShapefileName
	 * @param clippedShapefilePath
	 * @param clippedShapefileName
	 * @param llLng
	 * @param llLat
	 * @param urLng
	 * @param urLat
	 * @return
	 * @throws IOException
	 */
	public static IFeatureClass clip(String srcShapefilePath, String srcShapefileName, String clippedShapefilePath,
			String clippedShapefileName, double llLng, double llLat, double urLng, double urLat) throws IOException
	{
		// open source shapefile
		ShapefileWorkspaceFactory shapefileWorkspaceFactory = new ShapefileWorkspaceFactory();
		Workspace srcWorkspace = new Workspace(shapefileWorkspaceFactory.openFromFile(srcShapefilePath, 0));
		IFeatureClass srcFeatureClass = srcWorkspace.openFeatureClass(srcShapefileName);

		// create clip envelope to clip against the source features
		Envelope theClipEnvelope = new Envelope();
		theClipEnvelope.putCoords(llLng, llLat, urLng, urLat);

		// define a spatial query filter
		SpatialFilter spatialFilter = new SpatialFilter();
		spatialFilter.setGeometryByRef(theClipEnvelope);
		spatialFilter.setGeometryField(srcFeatureClass.getShapeFieldName());
		spatialFilter.setSpatialRel(esriSpatialRelEnum.esriSpatialRelIntersects);

		// get a cursor based on the spatial query for the source features
		IFeatureCursor srcFeatureCursor = srcFeatureClass.search(spatialFilter, true);

		// create target clip shapefile with same fields as source shapefile
		Workspace clippedWorkspace = new Workspace(shapefileWorkspaceFactory.openFromFile(clippedShapefilePath, 0));
		IFeatureClass clippedFeatureClass = clippedWorkspace.createFeatureClass(clippedShapefileName, srcFeatureClass
				.getFields(), srcFeatureClass.getCLSID(), srcFeatureClass.getEXTCLSID(), srcFeatureClass
				.getFeatureType(), srcFeatureClass.getShapeFieldName(), "");

		// create a cursor and buffer for the clipped features
		IFeatureCursor clippedFeaturesCursor = clippedFeatureClass.IFeatureClass_insert(true);
		IFeatureBuffer clippedFeaturesBuffer = clippedFeatureClass.createFeatureBuffer();
		//
		// Loop through the source features and copy them to the clipped shapefile.
		//
		IFeature srcIFeature = srcFeatureCursor.nextFeature();
		System.out.print("working: ");
		while (srcIFeature != null)
		{
			System.out.print(" . ");
			// get the source shape to clip. It may be a polygon, point, polyline, etc.
			IGeometry srcShapeGeometry = srcIFeature.getShapeCopy();
			//
			// Only an object that implements IToplogicalOperator can clip and
			// call buffer() on itself, so we check and then cast the geometry/shape to it,
			// If we knew we would always be dealing with a polygon, or polyline
			// or point or multipoint, etc, we could cast to it instead.
			//
			if (!(srcShapeGeometry instanceof ITopologicalOperator))
			{
				continue;
			}
			ITopologicalOperator srcShapeTopoGeom = (ITopologicalOperator) srcShapeGeometry;
			srcShapeTopoGeom.clip(theClipEnvelope);

			clippedFeaturesBuffer.setShapeByRef(srcShapeTopoGeom.buffer(0));
			//
			// Copy fields too, except the ID and geometry/shape fields
			//
			IFields srcFields = srcIFeature.getFields();
			IFields clippedFields = clippedFeaturesBuffer.getFields();
			int srcNFields = srcFields.getFieldCount();
			for (int i = 0; i < srcNFields; i++)
			{
				IField srcField = srcFields.getField(i);
				if (srcField.getType() == esriFieldType.esriFieldTypeOID
						|| srcField.getType() == esriFieldType.esriFieldTypeGeometry)
				{
					continue;
				}
				String srcFieldName = srcField.getName();
				int srcFieldNameIndex = srcFields.findField(srcFieldName);
				Object srcFeatureNameValue = srcIFeature.getValue(srcFieldNameIndex);
				int clippedFieldNameIndex = clippedFields.findField(srcFieldName);
				clippedFeaturesBuffer.setValue(clippedFieldNameIndex, srcFeatureNameValue);
			}
			clippedFeaturesCursor.insertFeature(clippedFeaturesBuffer);
			srcIFeature = srcFeatureCursor.nextFeature();
		}
		System.out.println();
		clippedFeaturesCursor.flush();
		return clippedFeatureClass;
	}
		
	/**
	 * Initializes the lowest available ArcGIS License
	 */
	private static void initializeArcGISLicenses(AoInitialize aoInit)
	{
		try
		{
			if (aoInit.isProductCodeAvailable(esriLicenseProductCode.esriLicenseProductCodeEngine) == esriLicenseStatus.esriLicenseAvailable)
			{
				aoInit.initialize(esriLicenseProductCode.esriLicenseProductCodeEngine);
			}
			else if (aoInit.isProductCodeAvailable(esriLicenseProductCode.esriLicenseProductCodeBasic) == esriLicenseStatus.esriLicenseAvailable)
			{
				aoInit.initialize(esriLicenseProductCode.esriLicenseProductCodeBasic);
			}
			else
			{
				System.err.println("Could not initialize an Engine or Basic License. Exiting application.");
				System.exit(-1);
			}
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}		

	/**
	 * Returns output directory
	 * @return
	 */
	private static String getOutputDir()
	{
		String userDir;
		if (System.getProperty("os.name").toLowerCase().indexOf("win") > -1)
			userDir = System.getenv("UserProfile");
		else
			userDir = System.getenv("HOME");
		String outputDir = userDir + File.separator + "arcgis_sample_output";
		System.out.println("Creating output directory - " + outputDir);
		new File(outputDir).mkdir();
		return outputDir;

	}
}
